package com.jiyongjun.component.condition;

import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.PropertyResolver;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * <h2></h2>
 * <h3>描述：</h3>
 *
 * @author yongjun.jyj
 * @since 2022/7/14 23:25
 */
public class SetPropertyCondition extends SpringBootCondition {
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnSetProperty.class.getName());
        AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(attributes);

        List<ConditionMessage> noMatch = new ArrayList<>();
        List<ConditionMessage> match = new ArrayList<>();

        ConditionOutcome outcome = determineOutcome(annotationAttributes, context.getEnvironment());
        (outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage());

        if (!noMatch.isEmpty()) {
            return ConditionOutcome.noMatch(ConditionMessage.of(noMatch));
        }
        return ConditionOutcome.match(ConditionMessage.of(match));
    }

    private ConditionOutcome determineOutcome(AnnotationAttributes attributes, PropertyResolver resolver) {
        String prefix = attributes.getString("prefix");
        if (StringUtils.hasText(prefix) && !prefix.endsWith(".")) {
            prefix = prefix + ".";
        }
        String key = prefix + attributes.getString("name");

        Set<String> property = resolver.getProperty(key, Set.class);
        if (property == null || property.isEmpty()) {
            if (attributes.getBoolean("matchIfMissing")) {
                return ConditionOutcome
                        .match(ConditionMessage.forCondition(ConditionalOnSetProperty.class).because("matchIfMissing=true"));
            } else {
                return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnSetProperty.class)
                        .didNotFind("property", "properties").items(ConditionMessage.Style.QUOTE, Collections.singletonList(key)));
            }
        }
        String contains = attributes.getString("contains");
        if (!contains.isEmpty()) {
            if (property.contains(contains)) {
                return ConditionOutcome
                        .match(ConditionMessage.forCondition(ConditionalOnSetProperty.class).because("contains is not empty, matched"));
            } else {
                return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnSetProperty.class)
                        .because("contains is not empty, unmatched"));
            }
        }

        String unContains = attributes.getString("unContains");
        if (!unContains.isEmpty()) {
            if (!property.contains(unContains)) {
                return ConditionOutcome
                        .match(ConditionMessage.forCondition(ConditionalOnSetProperty.class).because("unContains is not empty, matched"));
            } else {
                return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnSetProperty.class)
                        .because("unContains is not empty, unmatched"));
            }
        }

        return ConditionOutcome
                .match(ConditionMessage.forCondition(ConditionalOnSetProperty.class).because("matched"));
    }

}
